﻿#region  
//=====================================================
// 文件名: AssetBundleManager                
// 创建者: #AuthorName#                
// 创建时间: #CreateTime#              	
// Email: #AuthorEmail#               
// 描述: 当前脚本的功能              
// 修改者列表: 修改者名字以及修改功能      
// 版权所有: #Copyright#                 
//======================================================
#endregion 



using FrameWork.AB;
using FrameWork.ObjectPool;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;

namespace FrameWork
{
    public class AssetBundleManager : Singleton<AssetBundleManager>
    {
        private readonly Dictionary<uint, ResourceItem> m_ResourceItemDic = new Dictionary<uint, ResourceItem>();
        private readonly Dictionary<uint, AssetBundleItem> m_AssetBundleItemDic = new Dictionary<uint, AssetBundleItem>();
        private CalssObjectPool<AssetBundleItem> m_AssetBundleObjectPool = ObjectManager.Instance.GetOrCreateObjectPool<AssetBundleItem>(1000);

        public Dictionary<uint, ResourceItem> ResourceItemDic => m_ResourceItemDic;

        public bool LoadAssetBundleConfig()
        {
            m_ResourceItemDic.Clear();
            AssetBundle configAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/config");
            TextAsset textAsset = configAB.LoadAsset<TextAsset>("AssetBundleConfig.bytes");
            if (textAsset == null)
            {
                Debug.LogError("AssetBundleConfig  is no exist!");
                return false;
            }
            MemoryStream stream = new MemoryStream(textAsset.bytes);
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            AssetBundleConfig ABConfig = (AssetBundleConfig)binaryFormatter.Deserialize(stream);
            stream.Close();

            foreach (var item in ABConfig.ABList)
            {
                ResourceItem resItem = new ResourceItem();
                resItem.Crc = item.Crc;
                resItem.ABPackageName = item.ABPackageName;
                resItem.AssetName = item.AssetName;
                resItem.DependOfABPackages = item.ABDependce;
                if (m_ResourceItemDic.ContainsKey(resItem.Crc))
                {
                    Debug.LogError("Contains repeated CRC ,ABName:" + resItem.ABPackageName);
                }
                else
                {
                    m_ResourceItemDic.Add(resItem.Crc, resItem);
                }
            }
            return true;
        }

        public ResourceItem FindRecourceItem(uint _crc)
        {
            return m_ResourceItemDic[_crc];
        }

        public ResourceItem LoadResoureceAssetBundle(uint _crc)
        {
            if (!m_ResourceItemDic.TryGetValue(_crc, out ResourceItem resItem) || resItem == null)
            {
                Debug.LogError(string.Format("LoadResourceAssetBundle error : can not find crc {0} in assetbundlecinfig.", _crc.ToString()));
                return resItem;
            }
            if (resItem.LoadCompletedAssetBundle != null)
            {
                return resItem;
            }
            resItem.LoadCompletedAssetBundle = LoadAssetBundle(resItem.ABPackageName);
            if (resItem.DependOfABPackages != null)
            {
                foreach (var item in resItem.DependOfABPackages)
                {
                    LoadAssetBundle(item);
                }
            }
            return resItem;
        }

        public void ReleaseAsset(ResourceItem _item)
        {
            if (_item == null) return;

            if (_item.DependOfABPackages != null && _item.DependOfABPackages.Count > 0)
            {
                foreach (var item in _item.DependOfABPackages)
                {
                    UnLoadABPackage(item);
                }
            }

            if (_item.LoadCompletedAssetBundle!= null)
            {
                UnLoadABPackage(_item.ABPackageName);
            }
            
        }

        private AssetBundle LoadAssetBundle(string _name)
        {
            uint crc = CRC32.GetCRC32(_name);
            if (!m_AssetBundleItemDic.TryGetValue(crc, out AssetBundleItem item))
            {
                AssetBundle ab = null;
                string abPath = Application.streamingAssetsPath + "/" + _name;
                if (File.Exists(abPath))
                {
                    ab = AssetBundle.LoadFromFile(abPath);
                }

                if (ab == null)
                {
                    Debug.LogError(string.Format("LoadAssetBundleError:AssetBundlePath:{0}", abPath));
                }
                item = m_AssetBundleObjectPool.Spawn(true);
                item.assetBundle = ab;
                item.refCount++;
                m_AssetBundleItemDic.Add(crc, item);
            }
            else
            {
                item.refCount++;
            }

            return item.assetBundle;
        }

        private void UnLoadABPackage(string _name)
        {
            
            uint crc = CRC32.GetCRC32(_name);
            if (m_AssetBundleItemDic.TryGetValue(crc,out AssetBundleItem abItem) && abItem != null)
            {
                abItem.refCount--;
                if (abItem.refCount <=0)
                {
                    abItem.assetBundle.Unload(true);
                    abItem.Recycle();
                    m_AssetBundleObjectPool.Recycle(abItem);
                    m_AssetBundleItemDic.Remove(crc);
                }
            }
            else
            {
                Debug.LogErrorFormat("This AB package does not exist！CRC:{0},Name:{1}",crc,_name);
            }
        }
    }

    public class AssetBundleItem
    {
        public AssetBundle assetBundle;
        public int refCount;

        public void Recycle()
        {
            assetBundle = null;
            refCount = 0;
        }
    }

    public class ResourceItem
    {
        public uint Crc = 0;                                                   //资源路径CRC
        public string AssetName = string.Empty;
        public string ABPackageName = string.Empty;
        public List<string> DependOfABPackages = null;

        public AssetBundle LoadCompletedAssetBundle = null;
        public Object m_obj = null;
        public int m_Guid;
        public float m_LastUserTime = 0.0f;
        protected int m_RefCount = 0;
        public bool m_Clear = true;
        public int RefCount
        {
            get 
            { 
                return m_RefCount;
            }
            set
            {
                m_RefCount = value;
                if (m_RefCount<=0)
                {
                    //Debug.LogError(string.Format("ref count <= 0,count:{0};name:{1}.", m_RefCount, m_obj != null ? m_obj.name : "obj  name is  null"));
                }
            }
        }
    }
}